<?php

namespace App\Services;

class AliPayService
{
    //是否沙盒环境
    public $is_sandbox = false;
    //沙盒地址
    private $sandurl = 'https://openapi.alipaydev.com/gateway.do';
    //正式地址
    private $apiurl  = 'https://openapi.alipay.com/gateway.do';
    //网关地址
    private $gateway;
    //支付宝的APPID
    private $appid;
    //应用私钥
    private $rsaPrivateKey = '商户设置的私钥';
    //支付宝公钥
    private $alipayPublicKey= '支付宝自动生成的公钥';

    private $charset = 'utf-8';

    public function setAppid($appid)
    {
        $this->appid = $appid;
    }

    public function setRsaPrivateKey($rsaPrivateKey)
    {
        $this->rsaPrivateKey = $rsaPrivateKey;
    }

    public function setAlipayPublicKey($alipayPublicKey)
    {
        $this->alipayPublicKey = $alipayPublicKey;
    }
    //构造方法
    public function __construct(){
        $account = array(
            "appid"      => "2021003182681602",
            "privateKey" => "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCm6mHTef0Z2gdklgqDtbGlyiPpJWLB/BPDX10Th7mcrLuT+XB4hXt8qB1F/N6BBdPekG+pmKpYEMa1NNoQQZSnr+uYXeko+1u9iZe+/yg+3qh3hV1KRxXghHlYr+Ca1Ri4wccqu3AUWqdEyWcURn7qIzQOWf5Bn2II2xCxR5LJ9CJgbwBW8YuMjIX9SKSMy2/JVLqy2Ee5bIXDYy8xBRzIWUzQO6gkFbGlm8ugnrI8ZxI+UuvTVuKHO7kuQ3RKWizxrbskfBL5dRLNGuryr1S16WAZJailgNFz44FRRNEjPrGB2lET05gDkPy7EVWoNTwP1OazGcrFCI0LG/6z5N0ZAgMBAAECggEAHmEaXPse7c2Y+t20HDS9mIcp8g0z5EqU7kI1FpDlX3awzUXhCo6A4zdV3J8hgConXaK8MAnxNwYWpHRyG/NTsxF5t8mf3i2hjOW2ELUwBgFQeDB1wkcd3DdAPrwEDSorzbS3IzfqNkopl4Nwl2gdQVAW8WW/uQHhPethJmL4fHj+/qPXyW0+moNNXudyJ+FEBehwVBxE+xkrCLhPA99A+Mc/Z8xSyftfvGY35tzJSxXWV7d6YPNEUBX/gCc0+amT4lgTUZensLdw2HcwDw9Hu28J4zgHDOWUiNpCLQqGzuqgnsuZyjAKV4X3ThtdOaqGNELO8GaJqBnZcxhDxwPrgQKBgQDlrjOdwsiqJdxwiVFgTHKcYdpKnqxfZErjtVRMasFWn0KovCVQDpWYo0t1i89j34ZAOlSnMe8nqsWWp1isWFKhAKqOm1FouUGH+yHuGjDR9hkj6eywjcEhC0f5vE1qdppUReKp070cDNTje3Qnv3eQm1XQFbv5YtP0oL7hXfxLTQKBgQC6CvA3MrMYhWFy+vzo7FBUMdAnAo9L7icq21X5WUY+3NmvQl2K8U/QYtnx33uvt7l4nTGPeyrWFsnuiuTVUbIYIjRUVWwN07UjzdnmOCS9PVmmapaquTdUwZKJUFvC2DjkVEH8Sanf5paxoLyzNaqdOvTNvxK+Atak196ZJPs6/QKBgBtmHo4xUFLL1XIvT1/Xfsyc97dzGkj+49gqJ3Y+PfYhZYhPeXwFYV8mC7hoB0N4xMEgtDyFvmvS+WMxb2iP2LuQIsiESr/daa3ZDYEj6rMBUppwK2YcYu+YMuvdxLZwk5R+Cb4MnkCcX0EL1x1uv8qmXSRHk3b6jSJfkqW72n75AoGBALQKp+PTNc0SL3d1e9Hx998zAczuxjxmMhNghjgz9C4bzrK0GBDBbUsujtprO6iWtXALOA4x1QewsCUg10m1sRN37op6zIIpEpCewzPvu2ZD/AF3/20ip3BnkGdAlDzgwVX+UIlVxIcBAW7PLi6ETiOxDX4bXojx+EU4/5vXVSYFAoGAN3VSi9WQByoUrfZANskaO2jLMEqTMIo1fkoL5cLshEUU5eAG8ACpFEJMkCXjLCRAdTyRHgKGe+YQSLDwYyvFKVhGsuT5d0do3J63pV6oWG+1hyyGjMxif4WMHXF+wg+MFUezr3MaAOelc2wwN2qo8+6YwyZt3vBxvltP4imPVgE=",
            "publicKey"  => "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyCH93bXbSwzjl+1K8VBfeZylFgMzW39Xx984kion+K2u0Erp7QUC8zWVxtIGhzU1VbCORICHuokeUnJViwqGLt9zcGONCmEUFn7OcsSsv6gePPJ7H0+6J0ci3804sS1V7pRL1DG/5ZdmyqYo6xkK4yvVDcWx5DBUsBCrWbbMG8xGL8XZNIH3VKhVvvEnFaFemCeo77w4GizDmCpCOv8B8K4LE4p6nYqWLmvwD+aXhuu8Hi91zjDP2vnN2oG0HiIbT+bH60CvR48KnB1dwJlT9ws9xaH1lEUGYawO7ZVQ/hypaE9mI6kFFvi5atQIvj9y2zaaLMThLp8O2NxCIT+IMwIDAQAB"
        );
        //如果是沙箱测试
        if($this->is_sandbox){
            $this->gateway = $this->sandurl;
            $this->appid = '2021000117612368';
            $this->rsaPrivateKey ='MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDF0Bwt0712ogHbqp245rlz6Rj7LePxxhO4HoIUZhmZS7OeC1muhLK44e9kznFGT6YvOE2rasZT+EixPLifYqUML0Ecmxxvt3dr/WrAIIM941ODhjCaV+CDjZYNBZmsK+nPojFNWuyZCq9ygzX43XOjk3oTNbws5/tJdA4fzKIhlufuvpeGL5tgzoLKxUy2oweVluIyqkhFZUs9/9vgiUwFVt/iPf4JREGJ56Vq1grOBd+ZGqrljCcx6mEOm7GGd0lcDw038L//MYICdAgLfmCF+aojkRm3qkcTt7bl0jYsYnAWqxkBvtpCWQzFKMbwVsqbOM5EUzle8csNuguXqZ3TAgMBAAECggEAQXoSacTluOgFT23ZSUPt7QUiRLo68mOLUW2fxxrIrGpXaR/+rnq0Ieh/clG3QvvIWWb8ppnwTgkoHqqnvfJMCY12sv5Q2lXTTMaUWEYqywh9M1LusZgW89Sg72yEIm5itVzNjvpiyIG182SlL9w1k9aUGNm3kPG0xuqIZlQ48n7IZSeDGg+5ic4J+U7PGaCV99Q2zq7mkuIrbot+p478oFR7aM1/24OfxdJ1eh4FZZevwmLHpwqZEhecZFGrKx6/wlzDOhaiR7PgGcjH0/q6tiw6g5TCWfAzBDfFl/ZdRToNP6mOe57qt7jKhQz+2Lxz0qLNdNDacyTSYIkFg9cE0QKBgQD3AyFhrFk0VaTN2Xt6OAOEn1UIf1aBPenFQfV8+tIlbL5Io3VHHmycMuctW7c2BGkp7HF9OwcYGtURQdaBRCyKAYnW1nbZRgLHhkFC9rLyG04/yv6pN6/8EZ4bow7aUiCGoR/mV5GehiWTjb5/thCS+enaaalTdAfQBGZOl7VyGwKBgQDNArKycIbtXl2BEqcIqwW2nzv0RA8FhK7LkRy1CFIAeZfr1O4FiSXM/3V/EuocISCwOLWRq5Qyx7mZoMZ4FSRlQFbo9LUmYtIXtKHekwYZEpxleXJOVudRRQa3GkMvWAd3s1MSCGQ44jHFc/vbap6fxfR/GWHupoMOvW4N06h+qQKBgAFzekG5oniFPEedTHVmWNbxnK1FGjv+Ih5vicKnMo7XubJxi2HUkuzD+8mvbT8a/YcqhWwn6Z3BopjMWzc9MEnLQoUJk38pQyDq7/01t81mTapgRei2lAkWDWi6J38u1lQUJDzVLNzSiuv3/DOB3U0PvMj0r/L2jokxTWyOxZkbAoGBAK8DuqHyxn0lrhVeYxJXTg1Vas1gvKItXzuRqjwx2i9lnnhJ0tkO+CJFg3z0HL/e2BUYlIjDPUUMlDm+szAYfHWjs440OeGHQ2vRXM6yHOaSqMlIIHkYX2lV0CHIXcqxD870W9ptJ4IYN/0kwsHSB5DGYa+Pb7dYl1GiDa3oH1PhAoGBAPCSjFfXd45BPYDNL+4N9zS2wT5pjxlSo3Jrg3NKOqBZLS52ygZ6xeANXrG7lzitJ75prUS/JyXS83SM8au0vcO1Fko9BbW7MkuKFZH7FSpOlv5/PJQ6tI55TV7gGl8l4ND7V9OSdAMhEBQ/uT2gmddFoRTB14MeLIpe2iIVonan';
            $this->alipayPublicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs9AE75V+F242HcN/SlcGUxUNv3zGpXjai4u2ZMkOL1nolr4/v4BGEsE8tLaYq6lecG/of5taQ27WbWXB5PMB3s6emesTEn5yvZPh/HqmKACHi7js+Dtu123J/z0DyCukMm1ZHJe7jT9VEU9w4gIdNi64VxdaRF3ZMT6DXnboUEegyRKGmJc58h+O30P/UYqdCW/Gl+380o80e6Fs0rX33AixkNTRNgnQ3n2er1Nrqan/9sfUCnqqxgpc1+GTT+vyn3x4Xwvch2pRIkcK4BFCrWVVMKVXx/icj1njTVwWm1KYrXDVYrxf4ZLgpMHyW3SfhnXFKuxvSYnvm5EHjxtNEQIDAQAB';
        }else{
            $this->gateway = $this->apiurl;
            $this->appid = $account['appid'];
            $this->rsaPrivateKey = $account['privateKey'];
            $this->alipayPublicKey = $account['publicKey'];
        }
    }
    /**
     *
     * 发起当面付
     * $params 传输的数据
     */
    public function facepay($params){
        $biz_content = [
            "scene" =>"bar_code",//支付场景 条码支付，取值：bar_code 声波支付，取值：wave_code（必须）
            "out_trade_no" =>$params['out_trade_no'],//唯一标识，订单编号（必须）
            "total_amount" =>$params['money'],//订单金额（可选）
            "subject" =>$params['subject'],//商品名称
            //"seller_id" =>'',	//如果该值为空，则默认为商户签约账号对应的支付宝用户ID
            "timeout_express" =>"5m",//该笔订单允许的最晚付款时间，逾期将关闭交易。取值范围：1m～15d。m-分钟，h-小时，d-天
        ];
        //公共请求参数
        $param = [
            'app_id' => $this->appid,//支付宝分配给开发者的应用ID
            'method' => 'alipay.trade.precreate',//接口名称
            //'format' => 'JSON',//仅支持JSON
            'charset' => 'utf-8',//请求使用的编码格式
            'sign_type' => 'RSA2',//商户生成签名字符串所使用的签名算法类型，目前支持RSA2和RSA，推荐使用RSA2
            'sign' => '',//商户请求参数的签名串
            'timestamp' => date('Y-m-d H:i:s', time()),//发送请求的时间，格式"yyyy-MM-dd HH:mm:ss"
            'version' => '1.0',//调用的接口版本，固定为1.0
            'notify_url' => $params['notify_url'],//异步通知地址，支付宝服务器主动通知商户服务器里指定的页面http/https路径
            //'app_auth_token' => '',//app_auth_token
            'biz_content' => json_encode($biz_content),	//请求参数的集合，最大长度不限，除公共参数外所有请求参数都必须放在这个参数中传递
        ];
        //组合生成签名参数
        $signdata = [];
        $signdata['app_id'] = $param['app_id'];
        $signdata['method'] = $param['method'];
        $signdata['charset'] = $param['charset'];
        $signdata['sign_type'] = $param['sign_type'];
        $signdata['timestamp'] = $param['timestamp'];
        $signdata['version'] = $param['version'];
        $signdata['notify_url'] = $param['notify_url'];
        $signdata['biz_content'] = $param['biz_content'];
        //生成签名
        $sign = $this->generateSign($signdata, 'RSA2');

        $param['sign'] = $sign;
        //echo "<pre>";
        //发起请求
        $content = $this->curl($this->gateway,$param);

        $alipayData  = json_decode($content, true);
        //公共响应参数
        $responseData = $alipayData['alipay_trade_precreate_response'];
        if($responseData['code'] == 10000){
            //生成成功,返回结果给前端
            $data = [];
            $data['out_trade_no'] = $responseData['out_trade_no'];
            $data['qr_code'] = $responseData['qr_code'];
            return ['status' => true , 'msg' => '付款码生成成功' , 'data' => $data];
        }else {
            return ['status' => false , 'msg' => '错误码：' . $responseData['code'] . '，错误信息：' . $responseData['msg']];
        }
    }


    /**
     * 支付宝支付通知
     * @param $data 通知的数据
     */
    public function notify($data){
        $falg=false;
        //验证签名
        $param = $data;
        unset($param['sign']);
        unset($param['sign_type']);
        $rst = $this->rsaCheck($param, $data['sign'] , $data['sign_type']);
        if(!$rst){
            file_put_contents(LOG_PATH .'alipaynotify.log', '验签失败\r\n' , FILE_APPEND );
            return false;
        }
        //查询支付订单状态
        try{
            $rst = $this->orderquery($data, 'TRADE_SUCCESS');
        } catch (\Exception $e) {
            printLog("查询支付订单状态失败：".$e);
        }
        if($rst){
            $falg=true;
        }else {
            file_put_contents(LOG_PATH .'alipaynotify.log', '查询订单状态错误\r\n', FILE_APPEND);
            $falg=false;
        }
        return $falg;
    }

    /**
     *
     * 支付查询接口
     * @param  data   支付宝响应的参数集合
     * @param  status 要验证的状态
     * WAIT_BUYER_PAY 交易创建等待买家付款
     * TRADE_CLOSED   未付款交易超时关闭或支付完成后全额退款
     * TRADE_SUCCESS  交易支付成功
     * TRADE_FINISHED 交易结束不可退款
     */
    public function orderquery($data , $status){
        $biz_content = [
            'out_trade_no' => $data['out_trade_no'],
            'trade_no' => $data['trade_no'],
            //'org_pid' => '',
        ];

        $param = [
            'app_id' => $this->appid,
            'method' => 'alipay.trade.query',
            'charset' => 'utf-8',
            'sign_type' => 'RSA2',
            'sign' => '',
            'timestamp' => date('Y-m-d H:i:s',  time()),
            'version' => '1.0',
            'biz_content' => json_encode($biz_content),
        ];

        //组合签名数组
        $signdata = [];
        $signdata['app_id'] = $param['app_id'];
        $signdata['method'] = $param['method'];
        $signdata['charset'] = $param['charset'];
        $signdata['sign_type'] = $param['sign_type'];
        $signdata['timestamp'] = $param['timestamp'];
        $signdata['version'] = $param['version'];
        $signdata['biz_content'] = $param['biz_content'];

        //生成签名
        $sign = $this->generateSign($signdata, 'RSA2');
        $param['sign'] = $sign;
        $content = $this->curl($this->gateway,$param);
        $alipayData  = json_decode($content, true);
        //公共响应参数
        $responseData = $alipayData['alipay_trade_query_response'];
        if($responseData['code'] == 10000){
            if($responseData['trade_status'] == $status){
                return true;
            }else {
                return false;
            }
        }else {
            return false;
        }
    }

    public function generateSign($params, $signType = "RSA") {
        return $this->sign($this->getSignContent($params), $signType);
    }

    public function getSignContent($params) {
        ksort($params);
        $stringToBeSigned = "";
        $i = 0;
        foreach ($params as $k => $v) {
            if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) {
                // 转换成目标字符集
                $v = $this->characet($v, $this->charset);
                if ($i == 0) {
                    $stringToBeSigned .= "$k" . "=" . "$v";
                } else {
                    $stringToBeSigned .= "&" . "$k" . "=" . "$v";
                }
                $i++;
            }
        }
        unset ($k, $v);
        return $stringToBeSigned;
    }

    /**
     * 转换字符集编码
     * @param $data
     * @param $targetCharset
     * @return string
     */
    function characet($data, $targetCharset) {
        if (!empty($data)) {
            $fileType = $this->charset;
            if (strcasecmp($fileType, $targetCharset) != 0) {
                $data = mb_convert_encoding($data, $targetCharset, $fileType);
                //$data = iconv($fileType, $targetCharset.'//IGNORE', $data);
            }
        }
        return $data;
    }


    /**
     *
     * 校验$value是否非空
     */
    protected function checkEmpty($value) {
        if (!isset($value))
            return true;
        if ($value === null)
            return true;
        if (trim($value) === "")
            return true;
        return false;
    }


    /**
     *
     * 签名函数
     */
    protected function sign($data, $signType = "RSA") {
        $priKey=$this->rsaPrivateKey;
        $res = "-----BEGIN RSA PRIVATE KEY-----\n" .
            wordwrap($priKey, 64, "\n", true) .
            "\n-----END RSA PRIVATE KEY-----";
        ($res) or die('您使用的私钥格式错误，请检查RSA私钥配置');
        if ("RSA2" == $signType) {
            //OPENSSL_ALGO_SHA256是php5.4.8以上版本才支持
            openssl_sign($data, $sign, $res, version_compare(PHP_VERSION,'5.4.0', '<') ? SHA256 : OPENSSL_ALGO_SHA256);
        } else {
            openssl_sign($data, $sign, $res);
        }
        $sign = base64_encode($sign);
        return $sign;
    }

    /**
     *
     * 验签函数
     */
    public function rsaCheck($data, $sign,$type = 'RSA'){
        $public_key = $this->alipayPublicKey;
        $search = [
            "-----BEGIN PUBLIC KEY-----",
            "-----END PUBLIC KEY-----",
            "\n",
            "\r",
            "\r\n"
        ];
        $public_key=str_replace($search,"",$public_key);
        $public_key=$search[0] . PHP_EOL . wordwrap($public_key, 64, "\n", true) . PHP_EOL . $search[1];
        $res=openssl_get_publickey($public_key);
        if($res)
        {
            if($type == 'RSA'){
                $result = (bool)openssl_verify($this->getSignContent($data), base64_decode($sign), $res);
            }elseif($type == 'RSA2'){
                $result = (bool)openssl_verify($this->getSignContent($data), base64_decode($sign), $res,OPENSSL_ALGO_SHA256);
            }
            openssl_free_key($res);
        }else{
            return false;
        }
        return true;
    }
    /*
     * curl发送post请求，并返回请求头信息
     * url:       访问路径
     * postData:  要传递的post数据
     * refcode:   是否返回请求码
     * refheader: 是否返回请求头信息
     * */
    function curlPost($url, $postData, $refcode = false, $refheader = false) {
        $curl = curl_init();
        //设置提交的url
        curl_setopt($curl, CURLOPT_URL, $url);
        //设置获取的信息以文件流的形式返回，而不是直接输出
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        //忽略证书（关闭https验证）
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        //设置post方式提交
        curl_setopt($curl, CURLOPT_POST, 1);
        //设置post数据
        $postFields = http_build_query($postData);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $postFields);
        //添加请求头信息
        $headers = addHttpHeaders($url);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        //在尝试连接时等待的秒数
        curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30);
        //设置超时时间，最大执行时间超时时间（单位:s）
        curl_setopt($curl, CURLOPT_TIMEOUT, 300);
        //是否返回请求头信息（http协议头）
        if ($refheader) {
            curl_setopt($curl, CURLOPT_HEADER, 1);
            //追踪句柄的请求字符串（允许查看请求header）
            curl_setopt($curl, CURLINFO_HEADER_OUT, true);
        } else {
            curl_setopt($curl, CURLOPT_HEADER, 0);
        }
        //执行命令
        $result = curl_exec($curl);
        //转换字符编码
        $result = mb_convert_encoding($result, 'utf-8', 'UTF-8,GBK,GB2312,BIG5');
        //解决返回的json字符串中返回了BOM头的不可见字符（某些编辑器默认会加上BOM头）
        $result = trim($result,chr(239).chr(187).chr(191));
        //获取状态码
        $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        //关闭URL请求
        curl_close($curl);
        //是否返回请求码
        if ($refcode) {
            $html = array("httpcode" => $httpcode, "result" => $result);
            return $html;
        } else {
            return $result;
        }
    }


    function curl($url, $postFields = null) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_FAILONERROR, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

        $postBodyString = "";
        $encodeArray = Array();
        $postMultipart = false;
        $postCharset = 'UTF-8';

        if (is_array($postFields) && 0 < count($postFields)) {

            foreach ($postFields as $k => $v) {
                if ("@" != substr($v, 0, 1)) //判断是不是文件上传
                {

                    $postBodyString .= "$k=" . urlencode($this->characet($v, $postCharset)) . "&";
                    $encodeArray[$k] = $this->characet($v, $postCharset);
                } else //文件上传用multipart/form-data，否则用www-form-urlencoded
                {
                    $postMultipart = true;
                    $encodeArray[$k] = new \CURLFile(substr($v, 1));
                }

            }
            unset ($k, $v);
            curl_setopt($ch, CURLOPT_POST, true);
            if ($postMultipart) {
                curl_setopt($ch, CURLOPT_POSTFIELDS, $encodeArray);
            } else {
                curl_setopt($ch, CURLOPT_POSTFIELDS, substr($postBodyString, 0, -1));
            }
        }

        if ($postMultipart) {

            $headers = array('content-type: multipart/form-data;charset=' . $postCharset . ';boundary=' . $this->getMillisecond());
        } else {

            $headers = array('content-type: application/x-www-form-urlencoded;charset=' . $postCharset);
        }
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);




        $reponse = curl_exec($ch);

        if (curl_errno($ch)) {

            throw new Exception(curl_error($ch), 0);
        } else {
            $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            if (200 !== $httpStatusCode) {
                throw new Exception($reponse, $httpStatusCode);
            }
        }

        curl_close($ch);
        return $reponse;
    }
}
